﻿using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interactivity;
using nRoute.Components.Handlers;
#if (WINRT)
using Windows.UI.Xaml;
using nRoute.Behaviors.Interactivity;
using Windows.UI.Core;
using Windows.UI.Xaml.Input;
using Windows.System;
using Windows.UI.Xaml.Data;
#endif

namespace nRoute.Behaviors.Triggers
{
    public class KeyTrigger
#if (WINRT)
        : TriggerBase<FrameworkElement>
#else
        : TriggerBase<UIElement>
#endif
    {
        private static readonly Duration DURATION_ZERO = new Duration(TimeSpan.Zero);

        public static readonly DependencyProperty KeyEventProperty =
           DependencyProperty.Register("KeyEvent", typeof(KeyEventType), typeof(KeyTrigger),
           new PropertyMetadata(KeyEventType.KeyUp));

#if (WINRT)

        public static readonly DependencyProperty KeyProperty =
            DependencyProperty.Register("Key", typeof(VirtualKey), typeof(KeyTrigger),
            new PropertyMetadata(VirtualKey.None));

#else
        
        public static readonly DependencyProperty KeyCodeProperty =
            DependencyProperty.Register("KeyCode", typeof(int), typeof(KeyTrigger),
            new PropertyMetadata(0));

        public static readonly DependencyProperty KeyProperty =
            DependencyProperty.Register("Key", typeof(Key), typeof(KeyTrigger),
            new PropertyMetadata(Key.None));

#endif

        public static readonly DependencyProperty WithAltModifierProperty =
            DependencyProperty.Register("WithAltModifier", typeof(bool), typeof(KeyTrigger),
            new PropertyMetadata(false));

        public static readonly DependencyProperty WithControlModifierProperty =
            DependencyProperty.Register("WithControlModifier", typeof(bool), typeof(KeyTrigger),
            new PropertyMetadata(false));

        public static readonly DependencyProperty WithShiftModifierProperty =
            DependencyProperty.Register("WithShiftModifier", typeof(bool), typeof(KeyTrigger),
            new PropertyMetadata(false));

        public static readonly DependencyProperty WithWindowsModifierProperty =
            DependencyProperty.Register("WithWindowsModifier", typeof(bool), typeof(KeyTrigger),
            new PropertyMetadata(false));

        public static readonly DependencyProperty WithAppleModifierProperty =
            DependencyProperty.Register("WithAppleModifier", typeof(bool), typeof(KeyTrigger),
            new PropertyMetadata(false));

        public static readonly DependencyProperty SetIsHandledProperty =
            DependencyProperty.Register("SetIsHandled", typeof(bool), typeof(KeyTrigger),
            new PropertyMetadata(false));

        private bool _negate;

#region Properties

        [Category("Common Properties")]
        public KeyEventType KeyEvent
        {
            get { return (KeyEventType)(GetValue(KeyEventProperty)); }
            set { SetValue(KeyEventProperty, value); }
        }

#if (WINRT)

        [Category("Common Properties")]
        public VirtualKey Key
        {
            get { return (VirtualKey)(GetValue(KeyProperty)); }
            set { SetValue(KeyProperty, value); }
        }
#else

        [Category("Common Properties")]
        public int KeyCode
        {
            get { return Convert.ToInt32(GetValue(KeyCodeProperty)); }
            set { SetValue(KeyCodeProperty, value); }
        }

        [Category("Common Properties")]
        public Key Key
        {
            get { return (Key)(GetValue(KeyProperty)); }
            set { SetValue(KeyProperty, value); }
        }

#endif

        [Category("Common Properties")]
        public bool WithAltModifier
        {
            get { return Convert.ToBoolean(GetValue(WithAltModifierProperty)); }
            set { SetValue(WithAltModifierProperty, value); }
        }

        [Category("Common Properties")]
        public bool WithControlModifier
        {
            get { return Convert.ToBoolean(GetValue(WithControlModifierProperty)); }
            set { SetValue(WithControlModifierProperty, value); }
        }

        [Category("Common Properties")]
        public bool WithShiftModifier
        {
            get { return Convert.ToBoolean(GetValue(WithShiftModifierProperty)); }
            set { SetValue(WithShiftModifierProperty, value); }
        }

        [Category("Common Properties")]
        public bool WithWindowsModifier
        {
            get { return Convert.ToBoolean(GetValue(WithWindowsModifierProperty)); }
            set { SetValue(WithWindowsModifierProperty, value); }
        }

#if (SILVERLIGHT && !WP7)

        /// <remarks>Ignored in non-Silverlight Apps</remarks>
        [Category("Common Properties")]
        public bool WithAppleModifier
        {
            get { return Convert.ToBoolean(GetValue(WithAppleModifierProperty)); }
            set { SetValue(WithAppleModifierProperty, value); }
        }

#endif

        [Category("Common Properties")]
        public bool SetIsHandled
        {
            get { return Convert.ToBoolean(GetValue(SetIsHandledProperty)); }
            set { SetValue(SetIsHandledProperty, value); }
        }

        [Category("Common Properties")]
        public bool Negate
        {
            get { return _negate; }
            set { _negate = value; }
        }

#if (WINRT)

        [Category("Common Properties")]
        [CustomPropertyValueEditor(CustomPropertyValueEditor.PropertyBinding)]
        public Binding KeyEventBinding
        {
            get { return GetBinding(KeyEventProperty); }
            set { SetBinding<KeyEventType>(KeyEventProperty, value); }
        }

        [Category("Common Properties")]
        [CustomPropertyValueEditor(CustomPropertyValueEditor.PropertyBinding)]
        public Binding KeyBinding
        {
            get { return GetBinding(KeyProperty); }
            set { SetBinding<VirtualKey>(KeyProperty, value); }
        }

        [Category("Common Properties")]
        [CustomPropertyValueEditor(CustomPropertyValueEditor.PropertyBinding)]
        public Binding WithAltModifierBinding
        {
            get { return GetBinding(WithAltModifierProperty); }
            set { SetBinding<bool>(WithAltModifierProperty, value); }
        }

        [Category("Common Properties")]
        [CustomPropertyValueEditor(CustomPropertyValueEditor.PropertyBinding)]
        public Binding WithControlModifierBinding
        {
            get { return GetBinding(WithControlModifierProperty); }
            set { SetBinding<bool>(WithControlModifierProperty, value); }
        }

        [Category("Common Properties")]
        [CustomPropertyValueEditor(CustomPropertyValueEditor.PropertyBinding)]
        public Binding WithShiftModifierBinding
        {
            get { return GetBinding(WithShiftModifierProperty); }
            set { SetBinding<bool>(WithShiftModifierProperty, value); }
        }

        [Category("Common Properties")]
        [CustomPropertyValueEditor(CustomPropertyValueEditor.PropertyBinding)]
        public Binding WithWindowsModifierBinding
        {
            get { return GetBinding(WithWindowsModifierProperty); }
            set { SetBinding<bool>(WithWindowsModifierProperty, value); }
        }

        [Category("Common Properties")]
        [CustomPropertyValueEditor(CustomPropertyValueEditor.PropertyBinding)]
        public Binding SetIsHandledBinding
        {
            get { return GetBinding(SetIsHandledProperty); }
            set { SetBinding<bool>(SetIsHandledProperty, value); }
        }

#endif

#endregion

#region Overrides

        protected override void OnAttached()
        {
            base.OnAttached();

            if (KeyEvent == KeyEventType.KeyUp)
            {
                AssociatedObject.KeyUp += AssociatedObject_KeyUp;
            }
            else
            {
                AssociatedObject.KeyDown += AssociatedObject_KeyUp;
            }
        }

        protected override void OnDetaching()
        {
            if (KeyEvent == KeyEventType.KeyUp)
            {
                AssociatedObject.KeyUp -= AssociatedObject_KeyUp;
            }
            else
            {
                AssociatedObject.KeyDown -= AssociatedObject_KeyUp;
            }

           base.OnDetaching();
        }

#endregion

#region Handler

#if (WINRT)

        private void AssociatedObject_KeyUp(object sender, KeyRoutedEventArgs e)
        {
            var _virtualKey = e.Key;
            var _isMatch = e.Key.HasFlag(this.Key);

            // match the modifiers
            if (WithAltModifier && !_virtualKey.HasFlag(VirtualKey.Menu)) _isMatch = false;
            if (!WithAltModifier && _virtualKey.HasFlag(VirtualKey.Menu)) _isMatch = false;

            if (WithControlModifier && !_virtualKey.HasFlag(VirtualKey.Control)) _isMatch = false;
            if (!WithControlModifier && _virtualKey.HasFlag(VirtualKey.Control)) _isMatch = false;

            if (WithShiftModifier && !_virtualKey.HasFlag(VirtualKey.Shift)) _isMatch = false;
            if (!WithShiftModifier && _virtualKey.HasFlag(VirtualKey.Shift)) _isMatch = false;

            if (WithWindowsModifier && !(_virtualKey.HasFlag(VirtualKey.LeftWindows) || _virtualKey.HasFlag(VirtualKey.RightWindows))) _isMatch = false;
            if (!WithWindowsModifier && (_virtualKey.HasFlag(VirtualKey.LeftWindows) || _virtualKey.HasFlag(VirtualKey.RightWindows))) _isMatch = false;

            // we flow down till here, as we might need to negate the match
            if (Negate) _isMatch = !_isMatch;
            if (!_isMatch) return;

            // raise
            base.InvokeActions(_virtualKey);

            // handled
            if (SetIsHandled) e.Handled = true;
        }
            
#else

        private void AssociatedObject_KeyUp(object sender, KeyEventArgs e)
        {
             
#if SILVERLIGHT
            var _isMatch = (KeyCode != 0) ? (KeyCode == e.PlatformKeyCode) : (e.Key == Key);
#else
            var _isMatch = (KeyCode != 0) ? (KeyCode == Convert.ToInt32(e.Key)) : (e.Key == Key);
#endif

            // match the modifiers
            var _modifiers = Keyboard.Modifiers;

            if (WithAltModifier && (_modifiers & ModifierKeys.Alt) != ModifierKeys.Alt) _isMatch = false;
            if (!WithAltModifier && (_modifiers & ModifierKeys.Alt) == ModifierKeys.Alt) _isMatch = false;

            if (WithControlModifier && (_modifiers & ModifierKeys.Control) != ModifierKeys.Control) _isMatch = false;
            if (!WithControlModifier && (_modifiers & ModifierKeys.Control) == ModifierKeys.Control) _isMatch = false;

            if (WithShiftModifier && (_modifiers & ModifierKeys.Shift) != ModifierKeys.Shift) _isMatch = false;
            if (!WithShiftModifier && (_modifiers & ModifierKeys.Shift) == ModifierKeys.Shift) _isMatch = false;

            if (WithWindowsModifier && (_modifiers & ModifierKeys.Windows) != ModifierKeys.Windows) _isMatch = false;
            if (!WithWindowsModifier && (_modifiers & ModifierKeys.Windows) == ModifierKeys.Windows) _isMatch = false;

#if (SILVERLIGHT && !WP7)
            if (WithAppleModifier && (_modifiers & ModifierKeys.Apple) != ModifierKeys.Apple) _isMatch = false;
            if (!WithAppleModifier && (_modifiers & ModifierKeys.Apple) == ModifierKeys.Apple) _isMatch = false;
#endif

            // we flow down till here, as we might need to negate the match
            if (Negate) _isMatch = !_isMatch;
            if (!_isMatch) return;

            // raise
            base.InvokeActions(e.Key);

            // handled
            if (SetIsHandled) e.Handled = true;
        }

#endif

#endregion

    }
}
